package com.djl.zenithbite.service.user.impl;

import com.djl.zenithbite.common.constants.ResponseCodeConstants;
import com.djl.zenithbite.common.constants.VerifyCodeConstants;
import com.djl.zenithbite.common.request.BaseRequest;
import com.djl.zenithbite.common.response.BaseResponse;
import com.djl.zenithbite.common.threadlocal.UserContextHolder;
import com.djl.zenithbite.config.user.UserRoleEnum;
import com.djl.zenithbite.dto.request.user.UpdateUserInfoRequest;
import com.djl.zenithbite.dto.request.user.UserLoginRequest;
import com.djl.zenithbite.dto.request.user.UserRegisterRequest;
import com.djl.zenithbite.dto.response.UserLoginResponse;
import com.djl.zenithbite.entity.LoginCredentials;
import com.djl.zenithbite.entity.LoginCredentialsExample;
import com.djl.zenithbite.entity.User;
import com.djl.zenithbite.repository.LoginCredentialsMapper;
import com.djl.zenithbite.repository.UserMapper;
import com.djl.zenithbite.repository.impl.UserRepositoryImpl;
import com.djl.zenithbite.service.EnrollService;
import com.djl.zenithbite.service.user.UserCredentialsService;
import com.djl.zenithbite.service.user.UserService;
import com.djl.zenithbite.util.EncryptUtil;
import com.djl.zenithbite.util.RedisKeyUtil;
import com.djl.zenithbite.util.check.AssertUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.Cookie;
import java.util.Date;
import java.util.UUID;

/**
 * 用户服务实现类
 */
@Service
public class UserServiceImpl implements UserService {
    /**
     * 用户仓储服务
     */
    @Autowired
    private UserRepositoryImpl userRepository;

    @Autowired
    private LoginCredentialsMapper loginCredentialsMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${server.servlet.context-path}")
    String contextPath;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public BaseResponse<UserLoginResponse> login(UserLoginRequest request) {
        //check request
        checkLoginRequest(request);

        //check verify code
        //boolean verifyResult = EnrollService.inspectCheckCodeService(EnrollService.TRUE_CHECK_CODE,EnrollService.CREATE_TIME_CHECK,request.getVerifyCode(), request.getHttpSession());
        //AssertUtil.isTrue(verifyResult,"code verify error!");

        String codeKey;
        String ticket = request.getTicket();
        if (ticket == null) {
            codeKey = null;
        } else {
            codeKey = RedisKeyUtil.getCodeKey(ticket);
        }
        String realCode = (String) redisTemplate.opsForValue().get(codeKey);
        AssertUtil.isTrue(StringUtils.equals(realCode,request.getVerifyCode()),"verify code error");

        //get user info from db
        String userName = request.getUserName();
        User user = userRepository.selectByUserName(userName);
        AssertUtil.notNull(user,"user not exists");


        //verify password
        String password = request.getPassword();
        String userPwsalt = user.getUserPwsalt();

        AssertUtil.isTrue(StringUtils.equals(EncryptUtil.encryptPassword(password + userPwsalt),user.getUserPassword()),"user password error");

        //init holder
        UserContextHolder.init(user);


        //init token
        String token = UUID.randomUUID().toString().replaceAll("-","");
        LoginCredentials loginCredentials = new LoginCredentials();
        loginCredentials.setUserId(user.getUserId());
        loginCredentials.setCredential(token);
        loginCredentials.setCreatedAt(new Date());
        loginCredentials.setUpdatedAt(new Date());
        loginCredentials.setStatus((byte) 0);

        loginCredentialsMapper.insertSelective(loginCredentials);

        Cookie cookie = new Cookie("token", token);
        cookie.setMaxAge(7 * 24 * 60 * 60 * 1000);
        cookie.setPath(contextPath);
        request.getResponse().addCookie(cookie);

        BaseResponse<UserLoginResponse> response = new BaseResponse<>();
        response.setCode(ResponseCodeConstants.SUCCESS_CODE);

        UserLoginResponse userLoginResponse = new UserLoginResponse();
        userLoginResponse.setUserInfo(user);
        userLoginResponse.setToken(token);
        response.setData(userLoginResponse);
        return response;
    }

    @Override
    public BaseResponse logout(String token) {
        LoginCredentialsExample loginCredentialsExample = new LoginCredentialsExample();
        LoginCredentialsExample.Criteria criteria = loginCredentialsExample.createCriteria();
        criteria.andCredentialEqualTo(token);

        LoginCredentials loginCredentials = new LoginCredentials();
        loginCredentials.setUpdatedAt(new Date());
        loginCredentials.setStatus((byte)1);
        loginCredentialsMapper.updateByExampleSelective(loginCredentials,loginCredentialsExample);

        BaseResponse response = new BaseResponse();
        response.setCode(BaseResponse.SUCCESS);

        return response;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public BaseResponse register(UserRegisterRequest request) {

        //1. check request param
        checkRegisterRequest(request);


        //1.1 check user not exist
        User existsUser = userRepository.selectByUserEmail(request.getUserEmail());
        AssertUtil.isNull(existsUser,"Email already used!!!");

        User existsUser2 = userRepository.selectByUserName(request.getUserName());
        AssertUtil.isNull(existsUser2,"User already exists");


        //2.校验验证码
        //boolean b = EnrollService.inspectCheckCodeService(VerifyCodeConstants.REGISTER_VERIFY_CODE_KEY, VerifyCodeConstants.REGISTER_VERIFY_DATE_KEY, request.getVerifyCode(), request.getHttpSession());
        //AssertUtil.isTrue(b,"verify code error");
        String codeKey;
        String ticket = request.getTicket();
        if (ticket == null) {
            codeKey = null;
        } else {
            codeKey = RedisKeyUtil.getCodeKey(ticket);
        }
        Object codeObj = redisTemplate.opsForValue().get(codeKey);
        String realCode = codeObj != null ? codeObj.toString() : null;
        AssertUtil.isTrue(StringUtils.equals(realCode, request.getVerifyCode()), "verify code error");

        //3.build user
          User user = new User();
          user.setUserEmail(request.getUserEmail());
//        if(StringUtils.isNotBlank(request.getUserRole())){
//            user.setUserRole(UserRoleEnum.USER.getCode());
//        }else{
//            user.setUserRole(UserRoleEnum.valueOf(request.getUserRole()).getCode());
//        }

        if(StringUtils.isBlank(request.getUserRole())) {
            user.setUserRole(UserRoleEnum.USER.getCode());
        } else {
            try {
                user.setUserRole(UserRoleEnum.valueOf(request.getUserRole().toUpperCase()).getCode());
            } catch (IllegalArgumentException e) {
                user.setUserRole(UserRoleEnum.USER.getCode());
            }
        }

        user.setUserName(request.getUserName());
        user.setUserDesc(request.getUserDesc());
        user.setUserId(UUID.randomUUID().toString().replaceAll("-",""));
        String salt = EncryptUtil.generateSalt();
        user.setUserPwsalt(salt);
        user.setUserPassword(EncryptUtil.encryptPassword(request.getUserPassword() + salt));
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());

        userRepository.addUser(user);

        BaseResponse response = new BaseResponse();
        response.setCode(BaseResponse.SUCCESS);

        return response;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public BaseResponse resetPassword(UserRegisterRequest request){
        AssertUtil.isNotBlank(request.getUserPassword(),"password can not blank");
        AssertUtil.isNotBlank(request.getUserEmail(),"user email can not blank");

        User user = userRepository.selectByUserEmail(request.getUserEmail());
        AssertUtil.notNull(user,"user not exists");

        String salt = EncryptUtil.generateSalt();
        user.setUserPwsalt(salt);
        user.setUserPassword(EncryptUtil.encryptPassword(request.getUserPassword() + salt));

        userRepository.updateUserPsw(user);

        BaseResponse response = new BaseResponse();
        response.setCode(BaseResponse.SUCCESS);

        return response;
    }

    @Override
    public BaseResponse updateUserInfo(UpdateUserInfoRequest request) {
        User user = UserContextHolder.getUser();
        AssertUtil.notNull(user,"user can not be null");

        if (StringUtils.isNotBlank(request.getUserPhoto())){
            user.setUserPhoto(request.getUserPhoto());
        }

        if (StringUtils.isNotBlank(request.getUserPassword())){
            String salt = EncryptUtil.generateSalt();
            user.setUserPwsalt(salt);
            user.setUserPassword(EncryptUtil.encryptPassword(request.getUserPassword() + salt));
        }

        if (StringUtils.isNotBlank(request.getUserDesc())){
            user.setUserDesc(request.getUserDesc());
        }

        if (StringUtils.isNotBlank(request.getUserName())){
            user.setUserName(request.getUserName());
        }

        userRepository.updateUserPsw(user);

        BaseResponse response = new BaseResponse();
        response.setCode(BaseResponse.SUCCESS);
        response.setData(user);

        return response;
    }


    private void checkLoginRequest(UserLoginRequest request){
        AssertUtil.isNotBlank(request.getUserName(),"user name can not blank");
        AssertUtil.isNotBlank(request.getPassword(),"password can not blank");
        AssertUtil.isNotBlank(request.getVerifyCode(),"verifyCode can not blank");
        AssertUtil.notNull(request.getHttpSession(),"httpSession can not null");

    }

    private void checkRegisterRequest(UserRegisterRequest request){
        AssertUtil.isNotBlank(request.getUserName(),"user name can not blank");
        AssertUtil.isNotBlank(request.getUserPassword(),"password can not blank");
        AssertUtil.isNotBlank(request.getVerifyCode(),"verifyCode can not blank");
        AssertUtil.isNotBlank(request.getUserDesc(),"user can not blank");
        AssertUtil.isNotBlank(request.getUserDesc(),"userDesc can not blank");
        AssertUtil.notNull(request.getHttpSession(),"httpSession can not null");
    }
}
